﻿namespace KeyStone.Ioc
{
   using KeyStone.Ioc.Attributes;
   using System;

   /// <summary>
   /// Interface IIocContainer
   /// </summary>
   public interface IIocContainer : IDisposable
   {
      /// <summary>
      /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
      /// </summary>
      void Dispose();

      /// <summary>
      /// Registers and maps the specified interface to the concrete type.
      /// </summary>
      /// <param name="TInterface">The interface.</param>
      /// <param name="TType">Type to map.</param>
      /// <param name="life">The lifetime of the item being created.</param>
      /// <param name="constructorArgs">The constructor arguments.</param>
      /// <example>
      /// <code>
      /// IIocContainer container = new IocContainer();
      /// 
      /// container.Register(typeof(IMyType), typeof(MyType), Lifetime.PerInstance);
      /// 
      /// var myInstance =  container.Resolve&lt;IMyType&gt;();
      /// </code>
      /// </example>
      void Register(Type TInterface, Type TType, Lifetime life = Lifetime.PerInstance, params object[] constructorArgs);
      
      /// <summary>
      /// Registers the concrete type
      /// </summary>
      /// <param name="TType">Type to create</param>
      /// <param name="life">The lifetime of the item being created</param>
      /// <param name="constructorArgs">The constructor arguments.</param>
      /// <example>
      /// <code>
      /// IIocContainer container = new IocContainer();
      /// 
      /// container.Register(typeof(MyType), Lifetime.PerInstance);
      /// 
      /// var myInstance =  container.Resolve&lt;MyType&gt;();
      /// </code>
      /// </example>
      void Register(Type TType, Lifetime life = Lifetime.PerInstance, params object[] constructorArgs);
      
      /// <summary>
      /// Registers and maps the specified interface to the concrete type.
      /// </summary>
      /// <typeparam name="TInterface">The interface.</typeparam>
      /// <typeparam name="TType">Type to map.</typeparam>
      /// <param name="life">The lifetime of the item being created.</param>
      /// <param name="constructorArgs">The constructor arguments.</param>
      /// <example>
      /// <code>
      /// IIocContainer container = new IocContainer();
      /// 
      /// container.Register&lt;IMyType, MyType&gt;(Lifetime.PerInstance);
      /// 
      /// var myInstance =  container.Resolve&lt;IMyType&gt;();
      /// </code>
      /// </example>
      void Register<TInterface, TType>(Lifetime life = Lifetime.PerInstance, params object[] constructorArgs);
      
      /// <summary>
      /// Registers the specified interface to the concrete type.
      /// </summary>
      /// <typeparam name="TType">Type to map.</typeparam>
      /// <param name="life">The lifetime of the item being created.</param>
      /// <param name="constructorArgs">The constructor arguments.</param>
      /// <example>
      /// <code>
      /// IIocContainer container = new IocContainer();
      /// 
      /// container.Register&lt;IMyType&gt;(Lifetime.PerInstance);
      /// 
      /// var myInstance =  container.Resolve&lt;IMyType&gt;();
      /// </code>
      /// </example>
      void Register<TType>(Lifetime life = Lifetime.PerInstance, params object[] constructorArgs);
      
      /// <summary>
      /// Registers the specified instance
      /// </summary>
      /// <typeparam name="TType">The type of the t type.</typeparam>
      /// <param name="instance">The instance.</param>
      /// <example>
      /// <code>
      /// MyNewType mytype = new MyNewType();
      /// IIocContainer container = new IocContainer();
      /// 
      /// container.Register&lt;IMyType&gt;(Lifetime.PerInstance);
      /// 
      /// var myInstance =  container.Resolve&lt;MyNewType&gt;(mytype);
      /// </code>
      /// </example>
      void RegisterInstance<TType>(TType instance);
      
      /// <summary>
      /// Scans the current AppDomain and registers any types that are attributed with the specified type
      /// </summary>
      /// <typeparam name="TAttribute">The attribute to find</typeparam>
      /// <example>
      /// <code>
      /// [Main.cs]
      /// IIocContainer container = new IocContainer();
      /// 
      /// container.RegisterTypesOf&lt;RegisteredDependency&gt;();
      /// 
      /// var myInstance =  container.Resolve&lt;IMyType&gt;();
      /// 
      /// [MyClass.cs]
      /// 
      /// [RegisteredDependency(Lifetime = PerInstance)]
      /// public class MyClass
      /// {
      ///   // .... implementation
      /// }
      /// </code>
      /// </example>
      void RegisterTypesOf<TAttribute>() where TAttribute : RegisteredDependencyAttribute;

      /// <summary>
      /// Gets or sets a value indicating whether a type that has not been previously registered should be registered if a user attempts to
      /// resolve an instance of it.
      /// </summary>
      /// <value><c>true</c> if true, any type that has not been registered will be auto regitered when it is requested; otherwise, <c>false</c>.</value>
      /// <remarks>
      /// If a type is requested from the container that does not exist in the container, having this flag set will indicate that 
      /// the type should be imported into the container and then resolved for the user.  All types resolved in this manner will 
      /// automaticlly have a PerInstance lifetime.
      /// </remarks>
      /// <example>
      /// <code>
      /// [Main.cs]
      /// IIocContainer container = new IocContainer();
      /// container.RegisterUnResovedTypes = true;
      ///  
      /// var myInstance =  container.Resolve&lt;IMyType&gt;();
      /// 
      /// </code>
      /// </example>
      bool RegisterUnResolvedTypes { get; set; }
      
      /// <summary>
      /// Resolves the specified type.
      /// </summary>
      /// <param name="type">The type.</param>
      /// <returns>Object to resolve </returns>
      object Resolve(Type type);
     
      /// <summary>
      /// Resolves this instance.
      /// </summary>
      /// <typeparam name="TType">The type to resolve.</typeparam>
      /// <returns>TType.</returns>
      TType Resolve<TType>();
   }
}
